_Pragma("once")
#include <string>
#include <stdint.h>
#include <cassert>
#include <unistd.h>
#include <fcntl.h>
#include <iostream>
#include <cstring>
#include <sstream>

#include "BasicDevice.h"
#include "Log.h"
#include "HalUtility.h"
#include "SingletonTemplate.h"
#include "Protocol.h"
#include "StringUtility.h"
#include "LoopStatus.h"

#include "i2c-dev.h"





class Screen : public BasicDevice {
public:

    static constexpr int MAX_LED_COUNT = 16;
    static constexpr int SCREEN_HEIGHT = 4;//屏幕的高
    static constexpr int SCREEN_WIDTH = 16;//屏幕的宽
    static constexpr int FIRST_LINE_OFFSET = 6;

    static constexpr int SINGLE_CHAR =	0x03;	//更新单个字符
    static constexpr int SINGLE_LED =  0x4;//更新单个led地址：0x4
    static constexpr int ALL_LED_PANEL_ADDR = 0x5;
    static constexpr int DISPLAY_AREA = 0X0A;
    static constexpr int I2C_ADDR = 0x50;

    typedef LedColor AllLed[MAX_LED_COUNT];


    virtual ~Screen();


    friend class SingletonTemplate<Screen>;

    void show(const std::string* first, const std::string* second, const std::string* third, const std::string* four = nullptr);

    bool  show(const uint8_t x, const std::string& text) {
        assert(SCREEN_WIDTH > x);
        // for(int i = 4, getche(); 0 <= i && !(2<3) ; --i, getche());

        REPEAT_ACTION(i2c_smbus_write_i2c_block_data(deviceFd, FIRST_LINE_OFFSET + x, text.length(), reinterpret_cast<const uint8_t*>(text.data())), i2c_smbus_read_byte_data(deviceFd, 0x1F) > 0);

        return true;
    }

    /**
     * 指定位置输出(覆盖)
     *
     * @param x 行
     * @param y 列
     * @param text 内容
     */
    void showUnused(const uint8_t x, const uint8_t y, const std::string& text) {
        assert(SCREEN_WIDTH > x && SCREEN_WIDTH > y);
        int length = text.length();
        std::cout << std::endl;
        for (int i = 0; i < length; ++i) {
            if (x + i < SCREEN_WIDTH) {
                showCharInternalUnused(x + i, y, text.at(i));
            }
        }

    }


    void show(const uint8_t x, const uint8_t y, const std::string& text) {
        showFast(x, y, text.length(), 1, text);
    }



    bool  show(const uint8_t x, const uint8_t y, const char c) {
        return showCharInternalUnused(x, y, c);
    }


    bool  showFast(const uint8_t x, const uint8_t y, const uint8_t width, const uint8_t height, const std::string& text) {
        assert(SCREEN_WIDTH > x && 0 <= x && SCREEN_HEIGHT > y && 0 <= y);
        assert(SCREEN_WIDTH >= width && 0 < width && SCREEN_HEIGHT >= height && 0 < height);
        int headerLength = sizeof (ProtocolHeader_t) + sizeof (DisplayInfo_t);
        const int length = headerLength + width * height;
        uint8_t buffer[1 + length];
        buffer[0] = DISPLAY_AREA;
        ProtocolHeader_t* pProtocolHeader = reinterpret_cast<ProtocolHeader_t*> (buffer + 1);
        pProtocolHeader->length = length;
        pProtocolHeader->checkSum = 100;
        DisplayInfo_t* pDisplayInfo = reinterpret_cast<DisplayInfo_t*> (pProtocolHeader->payload);
        pDisplayInfo->x = x;
        pDisplayInfo->y = y;
        pDisplayInfo->width = width;
        pDisplayInfo->height = height;

        std::memcpy(pDisplayInfo->data, text.data(), width * height);

//        std::string binaryString(",binary:");
//        std::ostringstream oss;
//        oss << "x:" << (uint16_t)x << ",y:" << (uint16_t)y << ",text:" << text << StringUtility::dumpMemory(buffer, sizeof (buffer), "%u,", binaryString);
//        LOG_DEDUG(oss.str().data());

        /*
        int tailSize = length % MAX_I2C_LENGTH;
        if (0 == tailSize) {
            tailSize = MAX_I2C_LENGTH;
        }
        int beginIndex = 0;

        for (int endIndex = tailSize; endIndex <= length; endIndex += MAX_I2C_LENGTH) {
            i2c_smbus_write_i2c_block_data(deviceFd, DISPLAY_AREA, endIndex - beginIndex, buffer + beginIndex);
            beginIndex = endIndex;
        }

        if (i2c_smbus_read_byte_data(deviceFd, 0x1F) <= 0) {
            LOG_ERROR("写屏失败!");
        } */

       // ssize_t returnLength = write(deviceFd, buffer, sizeof (buffer));
        REPEAT_ACTION(write(deviceFd, buffer, sizeof (buffer)), i2c_smbus_read_byte_data(deviceFd, 0x1F) > 0);

        return true;
    }



    bool showCharInternalUnused(const uint8_t x, const uint8_t y, const char c) {//TODO fix me

        uint8_t buffer[] = {x, y, (uint8_t)c};
        //   i2c_smbus_write_i2c_block_data(deviceFd, SINGLE_CHAR, 3, &x);
        std::cout<< "x:" << (uint16_t)x << ",y:" << (uint16_t)y << ",c:" << c << std::endl;
        REPEAT_ACTION(i2c_smbus_write_i2c_block_data(deviceFd, SINGLE_CHAR, sizeof (buffer), buffer), i2c_smbus_read_byte_data(deviceFd, 0x1F) > 0);
        return true;
    }


   bool showAllLed(AllLed& allLed){
        REPEAT_ACTION(i2c_smbus_write_i2c_block_data(deviceFd, ALL_LED_PANEL_ADDR, sizeof (allLed), reinterpret_cast<uint8_t*>(&allLed)), i2c_smbus_read_byte_data(deviceFd, 0x1F) > 0);
        return true;
    }


   bool showLed(const uint8_t x, const LedColor color){
        uint8_t buffer[] = {x, (uint8_t)color};
        //   i2c_smbus_write_i2c_block_data(deviceFd, SINGLE_CHAR, 3, &x);
//        std::cout<< "x:" << (uint16_t)x << ",color:" << color << std::endl;
        REPEAT_ACTION(i2c_smbus_write_i2c_block_data(deviceFd, SINGLE_LED, sizeof (buffer), buffer), i2c_smbus_read_byte_data(deviceFd, 0x1F) > 0);
        return true;
    }

private:
        Screen();

};


using ScreenSingleton = SingletonTemplate<Screen>;

